﻿using Newtonsoft.Json;
using RuYiAdmin.Net.Common.Utility;
using RuYiAdmin.Net.Entity.BusinessEnum;
using RuYiAdmin.Net.Entity.CoreEnum;
using RuYiAdmin.Net.Entity.CoreExtensions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;

namespace RuYiAdmin.Net.Entity.CoreEntity
{
    /// <summary>
    /// 查询条件
    /// </summary>
    public class QueryCondition
    {
        /// <summary>
        /// 起始页
        /// </summary>
        public int PageIndex { get; set; }

        /// <summary>
        /// 分页数量
        /// </summary>
        public int PageSize { get; set; }

        /// <summary>
        /// 排序字段
        /// </summary>
        public String Sort { get; set; }

        /// <summary>
        /// 查询项
        /// </summary>
        public List<QueryItem> QueryItems { get; set; }

        #region 公有方法

        /// <summary>
        /// 转化为SQL语句
        /// </summary>
        /// <param name="queryItems">查询条件</param>
        /// <returns>字符串</returns>
        public static String ConvertToSQL(List<QueryItem> queryItems)
        {
            var queryStr = new StringBuilder();
            foreach (var item in queryItems)
            {
                queryStr.Append(ConvertQueryItem(item));
            }
            return queryStr.ToString();
        }

        /// <summary>
        /// 追加缺省查询条件
        /// </summary>
        /// <param name="queryCondition">QueryCondition对象</param>
        public static void AddDefaultQueryItem(QueryCondition queryCondition)
        {
            if (queryCondition.QueryItems.Count > 0)
            {
                queryCondition.QueryItems.Add(QueryItem.GetDefault());
            }
            else
            {
                queryCondition.QueryItems = new List<QueryItem>();
                queryCondition.QueryItems.Add(QueryItem.GetDefault());
            }
        }

        /// <summary>
        /// 查询条件转Lamda表达式
        /// </summary>
        /// <typeparam name="T">数据类型</typeparam>
        /// <param name="queryItems">查询条件</param>
        /// <returns>表达式</returns>
        public static Expression<Func<T, bool>> BuildExpression<T>(List<QueryItem> queryItems)
        {
            Expression<Func<T, bool>> where = PredicateExtension.True<T>();

            foreach (var queryItem in queryItems)
            {
                var field = queryItem.Field;
                var queryMethod = queryItem.QueryMethod;
                var dataType = queryItem.DataType;
                var value = queryItem.Value;

                if (String.IsNullOrEmpty(field) || value == null)
                {
                    continue;
                }

                //构建参数
                var parameter = Expression.Parameter(typeof(T), "p");

                Expression constant = null;

                //表达式左侧 like: p.Name
                Expression left = Expression.PropertyOrField(parameter, field);

                //表达式右侧，比较值， like '张三'
                Expression right = Expression.Constant(value);

                #region 特殊值处理

                if (right.Type.Equals(typeof(System.Text.Json.JsonElement)))
                {
                    right = Expression.Constant(value.ToString());
                }

                if (left.Type.Equals(typeof(OperationType)))
                {
                    right = Expression.Constant((OperationType)int.Parse(value.ToString()), typeof(OperationType));
                }
                else if (left.Type.Equals(typeof(DateTime)))
                {
                    right = Expression.Constant(DateTime.Parse(value.ToString()), typeof(DateTime));
                }
                else if (left.Type.Equals(typeof(Guid)))
                {
                    right = Expression.Constant(Guid.Parse(value.ToString()), typeof(Guid));
                }
                else if (left.Type.Equals(typeof(float)))
                {
                    right = Expression.Constant(float.Parse(value.ToString()), typeof(float));
                }
                else if (left.Type.Equals(typeof(Double)))
                {
                    right = Expression.Constant(Double.Parse(value.ToString()), typeof(Double));
                }
                #endregion

                switch (queryMethod)
                {
                    case QueryMethod.Equal:
                        constant = Expression.Equal(left, right);
                        break;

                    case QueryMethod.LessThan:
                        constant = Expression.LessThan(left, right);
                        break;

                    case QueryMethod.LessThanOrEqual:
                        constant = Expression.LessThanOrEqual(left, right);
                        break;

                    case QueryMethod.GreaterThan:
                        constant = Expression.GreaterThan(left, right);
                        break;

                    case QueryMethod.GreaterThanOrEqual:
                        constant = Expression.GreaterThanOrEqual(left, right);
                        break;

                    case QueryMethod.BetweenAnd:
                        var arr = value.ToString().Split(',');

                        constant = Expression.GreaterThanOrEqual(left, Expression.Constant(arr[0]));
                        var lambda = Expression.Lambda<Func<T, Boolean>>(constant, parameter);
                        @where = @where.And(lambda);

                        constant = Expression.LessThanOrEqual(left, Expression.Constant(arr[1]));
                        lambda = Expression.Lambda<Func<T, Boolean>>(constant, parameter);
                        @where = @where.And(lambda);
                        constant = null;
                        break;

                    case QueryMethod.Like:
                    case QueryMethod.Include:
                        var method = dataType == DataType.Int ?
                            typeof(List<int>).GetMethod("Contains", new Type[] { typeof(List<int>) }) :
                            typeof(String).GetMethod("Contains", new Type[] { typeof(String) });

                        constant = Expression.Call(left, method, right);
                        break;

                    case QueryMethod.OrLike:
                        var methodOrLike = dataType == DataType.Int ?
                            typeof(List<int>).GetMethod("Contains", new Type[] { typeof(List<int>) }) :
                            typeof(String).GetMethod("Contains", new Type[] { typeof(String) });

                        constant = Expression.Call(left, methodOrLike, right);
                        lambda = Expression.Lambda<Func<T, Boolean>>(constant, parameter);
                        @where = @where.Or(lambda);

                        constant = null;
                        break;

                    case QueryMethod.NotEqual:
                        constant = Expression.NotEqual(left, right);
                        break;

                    default:
                        break;
                }

                if (constant != null)
                {
                    var lambda = Expression.Lambda<Func<T, Boolean>>(constant, parameter);
                    @where = @where.And(lambda);
                }
            }
            return @where;
        }

        /// <summary>
        /// 反序列化
        /// </summary>
        /// <param name="value"></param>
        /// <returns>QueryCondition</returns>
        public static QueryCondition DeserializeObject(string value)
        {
            return JsonConvert.DeserializeObject<QueryCondition>(value);
        }

        #endregion

        #region 私有方法

        /// <summary>
        /// 条件转化
        /// </summary>
        /// <param name="queryItem">查询条件</param>
        /// <returns>字符串</returns>
        private static StringBuilder ConvertQueryItem(QueryItem queryItem)
        {
            var condition = new StringBuilder();

            switch (queryItem.QueryMethod)
            {
                case QueryMethod.Equal:
                    if (queryItem.DataType.Equals(DataType.Date))
                    {
                        condition.Append($" and {queryItem.Field}={DateUtil.ParseToDate(DateTime.Parse(queryItem.Value.ToString()))}");
                    }
                    else if (queryItem.DataType.Equals(DataType.DateTime))
                    {
                        condition.Append($" and {queryItem.Field}={DateUtil.ParseToDateTime(DateTime.Parse(queryItem.Value.ToString()))}");
                    }
                    else
                    {
                        condition.Append($" and {queryItem.Field}='{queryItem.Value}'");
                    }
                    break;

                case QueryMethod.Like:
                    condition.Append($" and {queryItem.Field} like '%{queryItem.Value}%'");
                    break;

                case QueryMethod.LessThan:
                    condition.Append($" and {queryItem.Field}<'{queryItem.Value}'");
                    break;

                case QueryMethod.LessThanOrEqual:
                    condition.Append($" and {queryItem.Field}<='{queryItem.Value}'");
                    break;

                case QueryMethod.GreaterThan:
                    condition.Append($" and {queryItem.Field}>'{queryItem.Value}'");
                    break;

                case QueryMethod.GreaterThanOrEqual:
                    condition.Append($" and {queryItem.Field}>='{queryItem.Value}'");
                    break;

                case QueryMethod.BetweenAnd:
                    var array = queryItem.Value.ToString().Split(',');
                    if (queryItem.DataType.Equals(DataType.Date))
                    {
                        condition.Append($" and {queryItem.Field} between {DateUtil.ParseToDate(DateTime.Parse(array[0]))} "
                             + $" and {DateUtil.ParseToDate(DateTime.Parse(array[1]))}");
                    }
                    else if (queryItem.DataType.Equals(DataType.DateTime))
                    {
                        condition.Append($" and {queryItem.Field} between {DateUtil.ParseToDateTime(DateTime.Parse(array[0]))} "
                            + $" and {DateUtil.ParseToDateTime(DateTime.Parse(array[1]))}");
                    }
                    else
                    {
                        condition.Append($" and {queryItem.Field} between '{array[0]}' and '{array[1]}'");
                    }
                    break;

                case QueryMethod.Include:
                    condition.Append($" and {queryItem.Field} in ({queryItem.Value})");
                    break;

                case QueryMethod.OrLike:
                    condition.Append($" or {queryItem.Field} like '%{queryItem.Value}%'");
                    break;

                case QueryMethod.NotEqual:
                    condition.Append($" and {queryItem.Field} <> '{queryItem.Value}'");
                    break;

                default: break;
            }

            return condition;
        }

        #endregion
    }
}
